package ast

import (
	"fmt"
	"strings"
)

type TokenType string

const (
	// 关键词
	KeywordToken TokenType = "KEYWORD" // 如 SELECT, FROM, WHERE, INSERT, UPDATE, DELETE
	//标识符
	IdentifierToken TokenType = "IDENTIFIER" // 表名、列名、别名等
	//符号
	SymbolToken TokenType = "SYMBOL" //如 ',', '(', ')', '=', '>', '<' 用于 SQL 语句中的结构性分隔，如列名、表名之间的逗号，条件表达式的括号，赋值操作等。
	//数字
	NumberToken TokenType = "NUMBER" // 如 123, 3.14
	//字符串
	StringToken TokenType = "STRING" // 如 'hello', 'world'
	//操作符
	OperatorToken TokenType = "OPERATOR" //如 '+', '-', '*', '/', '%' 表示用于操作的符号（数学、逻辑、比较）
	CommentToken  TokenType = "COMMENT"  // 单行或多行注释
	//未知
	UnknownToken TokenType = "UNKNOWN" // 未知的标记类型
	//错误
	ErrorToken TokenType = "ERROR" //错误
)

type Token struct {
	Type  TokenType
	Value string
}

type Lexicaler struct {
	input string
	pos   int
}

func NewLexicaler(input string) *Lexicaler {
	return &Lexicaler{input: input}
}

// 跳过空格
func (l *Lexicaler) skipWhitespace() {
	for l.pos < len(l.input) && isWhitespace(l.input[l.pos]) {
		l.pos++
	}
}

func (l *Lexicaler) NextToken() *Token {
	l.skipWhitespace()
	if l.pos >= len(l.input) {
		return nil
	}
	char := l.input[l.pos]
	//处理关键词
	if isLetter(char) {
		start := l.pos
		for l.pos < len(l.input) && (isLetter(l.input[l.pos]) || isDigit(l.input[l.pos])||l.input[l.pos]=='_') {
			l.pos++
		}
		word := l.input[start:l.pos]
		if isKeyword(word) {
			return &Token{Type: KeywordToken, Value: strings.ToUpper(word)}
		}
		return &Token{Type: IdentifierToken, Value: word}
	}else if char=='`'&&l.pos+1 < len(l.input) && isLetter(l.input[l.pos+1]){
		//处理`sys_admin`这种情况的
		start := l.pos
		l.pos++
		for l.pos < len(l.input) && l.input[l.pos]!='`'{
			l.pos++
		}
		l.pos++
		word := l.input[start:l.pos]
		return &Token{Type: IdentifierToken, Value: word}
	}
	//处理数字
	if isDigit(char) {
		start := l.pos
		for l.pos < len(l.input) && isDigit(l.input[l.pos]) {
			l.pos++
		}
		return &Token{Type: NumberToken, Value: l.input[start:l.pos]}
	}
	//处理操作符
	if isOperator(char) {
		start := l.pos
		l.pos++
		// fmt.Println("xxx=",l.input[start:l.pos])
		if l.pos < len(l.input) && (l.input[start:l.pos+1] == ">=" || l.input[start:l.pos+1] == "<=" || l.input[start:l.pos+1] == "<>" || l.input[start:l.pos+1] == "!=") {
			l.pos++
			return &Token{Type: OperatorToken, Value: l.input[start:l.pos]}
		}
		return &Token{Type: OperatorToken, Value: string(char)}
	}
	//处理符号
	if isSymbol(char) {
		l.pos++
		return &Token{Type: SymbolToken, Value: string(char)}
	}
	//特殊判断
	if char == '*' {
		start := l.pos
		l.pos++
		if isInSelectStatement(strings.ToLower(l.input[:start])) {
			return &Token{Type: SymbolToken, Value: string(char)}
		}
		return &Token{Type: OperatorToken, Value: string(char)}
	}
	//处理字符串
	if char == '\'' {
		l.pos++
		start := l.pos
		for l.pos < len(l.input) && l.input[l.pos] != '\'' {
			l.pos++
		}
		words := l.input[start:l.pos]
		l.pos++
		return &Token{Type: StringToken, Value: words}
	} else if char == '"' {
		l.pos++
		start := l.pos
		for l.pos < len(l.input) && l.input[l.pos] != '"' {
			l.pos++
		}
		words := l.input[start:l.pos]
		l.pos++
		return &Token{Type: StringToken, Value: words}
	}
	//处理注释
	if char == '-' && l.pos+1 < len(l.input) && l.input[l.pos+1] == '-' {
		start := l.pos
		for l.pos < len(l.input) && l.input[l.pos] != '\n'&&  l.input[l.pos] != '\r'{
			l.pos++
		}
		return &Token{Type: CommentToken, Value: l.input[start:l.pos]}
	} else if char == '-' {
		//单独处理-号
		l.pos++
		return &Token{Type: OperatorToken, Value: string(char)}
	} else if char == '/' && l.pos+1 < len(l.input) && l.input[l.pos+1] == '*' {
		//处理多行注释
		start := l.pos
		l.pos += 2
		for l.pos+1 < len(l.input) && !(l.input[l.pos] == '*' && l.input[l.pos+1] == '/') {
			l.pos++
		}
		if l.pos+1 < len(l.input) {
			l.pos += 2
			return &Token{Type: CommentToken, Value: l.input[start:l.pos]}
		} else {
			return &Token{Type: ErrorToken, Value: "错误注释"}
		}
	} else if char == '/' {
		//单独处理/号
		l.pos++
		return &Token{Type: OperatorToken, Value: string(char)}
	} else if char == '#' {
		start := l.pos
		for l.pos < len(l.input) && l.input[l.pos] != '\n'  && l.input[l.pos] != '\r'{
			l.pos++
		}
		return &Token{Type: CommentToken, Value: l.input[start:l.pos]}
	}
	l.pos++
	fmt.Println("未知",string(char))
	return &Token{Type: UnknownToken, Value: string(char)}
}

func(l *Lexicaler)GetTokens()[]*Token{
	var tokens []*Token
	for {
		token:=l.NextToken()
		if token==nil{
			break
		}
		tokens=append(tokens, token)
	}
	return tokens
}

// 是否是关键词
func isKeyword(word string) bool {
	keywords := []string{
		"accessible",
		"add",
		"against",
		"all",
		"alter",
		"analyze",
		"and",
		"as",
		"asc",
		"asensitive",
		"auto_increment",
		"before",
		"begin",
		"between",
		"bigint",
		"binary",
		"_binary",
		"bit",
		"blob",
		"bool",
		"boolean",
		"both",
		"by",
		"call",
		"cascade",
		"case",
		"cast",
		"change",
		"char",
		"character",
		"charset",
		"check",
		"collate",
		"column",
		"comment",
		"committed",
		"commit",
		"condition",
		"constraint",
		"continue",
		"convert",
		"substr",
		"substring",
		"create",
		"cross",
		"current_date",
		"current_time",
		"current_timestamp",
		"current_user",
		"cursor",
		"database",
		"databases",
		"day_hour",
		"day_microsecond",
		"day_minute",
		"day_second",
		"date",
		"datetime",
		"dec",
		"decimal",
		"declare",
		"default",
		"delayed",
		"delete",
		"desc",
		"describe",
		"deterministic",
		"distinct",
		"distinctrow",
		"div",
		"double",
		"drop",
		"duplicate",
		"each",
		"else",
		"elseif",
		"enclosed",
		"end",
		"enum",
		"escape",
		"escaped",
		"exists",
		"exit",
		"explain",
		"expansion",
		"extended",
		"false",
		"fetch",
		"float",
		"float4",
		"float8",
		"for",
		"force",
		"foreign",
		"from",
		"full",
		"fulltext",
		"generated",
		"geometry",
		"geometrycollection",
		"get",
		"global",
		"grant",
		"group",
		"group_concat",
		"having",
		"high_priority",
		"hour_microsecond",
		"hour_minute",
		"hour_second",
		"if",
		"ignore",
		"in",
		"index",
		"infile",
		"inout",
		"inner",
		"insensitive",
		"insert",
		"int",
		"int1",
		"int2",
		"int3",
		"int4",
		"int8",
		"integer",
		"interval",
		"into",
		"io_after_gtids",
		"is",
		"isolation",
		"iterate",
		"join",
		"json",
		"key",
		"keys",
		"key_block_size",
		"kill",
		"language",
		"last_insert_id",
		"leading",
		"leave",
		"left",
		"less",
		"level",
		"like",
		"limit",
		"linear",
		"lines",
		"linestring",
		"load",
		"localtime",
		"localtimestamp",
		"lock",
		"long",
		"longblob",
		"longtext",
		"loop",
		"low_priority",
		"master_bind",
		"match",
		"maxvalue",
		"mediumblob",
		"mediumint",
		"mediumtext",
		"middleint",
		"minute_microsecond",
		"minute_second",
		"mod",
		"mode",
		"modifies",
		"multilinestring",
		"multipoint",
		"multipolygon",
		"names",
		"natural",
		"nchar",
		"next",
		"not",
		"no_write_to_binlog",
		"null",
		"numeric",
		"offset",
		"on",
		"only",
		"optimize",
		"optimizer_costs",
		"option",
		"optionally",
		"or",
		"order",
		"out",
		"outer",
		"outfile",
		"partition",
		"point",
		"polygon",
		"precision",
		"primary",
		"processlist",
		"procedure",
		"query",
		"range",
		"read",
		"reads",
		"read_write",
		"real",
		"references",
		"regexp",
		"release",
		"rename",
		"reorganize",
		"repair",
		"repeat",
		"repeatable",
		"replace",
		"require",
		"resignal",
		"restrict",
		"return",
		"revoke",
		"right",
		"rlike",
		"rollback",
		"schema",
		"schemas",
		"second_microsecond",
		"select",
		"sensitive",
		"separator",
		"serializable",
		"session",
		"set",
		"share",
		"show",
		"signal",
		"signed",
		"smallint",
		"spatial",
		"specific",
		"sql",
		"sqlexception",
		"sqlstate",
		"sqlwarning",
		"sql_big_result",
		"sql_cache",
		"sql_calc_found_rows",
		"sql_no_cache",
		"sql_small_result",
		"ssl",
		"start",
		"starting",
		"status",
		"stored",
		"straight_join",
		"stream",
		"table",
		"tables",
		"terminated",
		"text",
		"than",
		"then",
		"time",
		"timestamp",
		"tinyblob",
		"tinyint",
		"tinytext",
		"to",
		"trailing",
		"transaction",
		"trigger",
		"true",
		"truncate",
		"uncommitted",
		"undo",
		"union",
		"unique",
		"unlock",
		"unsigned",
		"update",
		"usage",
		"use",
		"using",
		"utc_date",
		"utc_time",
		"utc_timestamp",
		"values",
		"variables",
		"varbinary",
		"varchar",
		"varcharacter",
		"varying",
		"virtual",
		"vindex",
		"vindexes",
		"view",
		"vitess_keyspaces",
		"vitess_shards",
		"vitess_tablets",
		"vschema_tables",
		"when",
		"where",
		"while",
		"with",
		"write",
		"xor",
		"year",
		"year_month",
		"zerofill",
	}
	for _, keyword := range keywords {
		if strings.EqualFold(word, keyword) {
			return true
		}
	}
	return false
}

// 是不是操作符号
func isOperator(char byte) bool {
	operators := "+%=<>!"
	return strings.ContainsRune(operators, rune(char))
}

// 判断是不是字母
func isLetter(char byte) bool {
	return (char >= 'a' && char <= 'z') || (char >= 'A' && char <= 'Z')
}

// 判断是不是数字
func isDigit(char byte) bool {
	return (char >= '0' && char <= '9')
}

// 判断是否是符号
func isSymbol(char byte) bool {
	symbols := ",();"
	return strings.ContainsRune(symbols, rune(char))
}

// 判断是否是空格
func isWhitespace(char byte) bool {
	return (char == ' ' || char == '\t'|| char == '\n'|| char == '\r')
}

// 是否在select中
func isInSelectStatement(preString string) bool {
	selectKeyWord := "select"
	pos := len(preString) - 1
	if pos >= 0 {
		findChar := false
		end := len(preString) - 1
		for pos >= 0 {
			if !isWhitespace(preString[pos]) && !findChar {
				findChar = true
				end = pos + 1
				// fmt.Println("1:"+string(preString[pos]))
			} else if isWhitespace(preString[pos]) && findChar {
				// fmt.Println("2:"+string(preString[pos]))
				break
			}
			pos--
		}
		if preString[pos+1:end] == selectKeyWord {
			return true
		}
	}
	return false
}
